﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace CardPlay.Client.Controls
{
    public static class Extensions
    {
        /// <summary>
        /// A simple iterator method to expose the visual tree to LINQ
        /// </summary>
        /// <param name="start"></param>
        /// <param name="eval"></param>
        /// <returns></returns>
        public static IEnumerable<T> EnumerateVisualTree<T>(this T start, Predicate<T> eval) where T : DependencyObject
        {
            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++)
            {
                var child = VisualTreeHelper.GetChild(start, i) as T;
                if (child == null || (eval == null || !eval(child))) continue;

                yield return child;

                foreach (var childOfChild in EnumerateVisualTree(child, eval))
                    yield return childOfChild;
            }
        }

        /// <summary>
        /// Locate the first real focusable child.  We keep going down
        /// the visual tree until we hit a leaf node.
        /// </summary>
        /// <param name="firstElement"></param>
        /// <returns></returns>
        public static IInputElement GetLeafFocusableChild(this IInputElement firstElement)
        {
            var prevElement = GetFirstFocusableChild(firstElement);

            for (var nextElement = prevElement; nextElement != null;)
            {
                prevElement = nextElement;
                nextElement = GetFirstFocusableChild(nextElement);
            }

            return prevElement;
        }

        /// <summary>
        /// This searches the Visual Tree looking for a valid child which can have focus.
        /// </summary>
        /// <param name="firstElement"></param>
        /// <returns></returns>
        public static IInputElement GetFirstFocusableChild(this IInputElement firstElement)
        {
            var dependencyObject = firstElement as DependencyObject;
            return dependencyObject == null
                ? null
                : dependencyObject.EnumerateVisualTree(c => !FocusManager.GetIsFocusScope(c))
                    .OfType<IInputElement>()
                    .FirstOrDefault(ie => ie.Focusable && ie.IsEnabled && (!(ie is FrameworkElement) || ((FrameworkElement)ie).IsVisible));
        }

        public static IObservable<EventPattern<MouseEventArgs>> GetLostMouseCapture(this UIElement uiElement)
        {
            return Observable.FromEventPattern<MouseEventArgs>(uiElement, "LostMouseCapture");
        }

        public static IObservable<EventPattern<MouseEventArgs>> GetMouseEnter(this UIElement uiElement)
        {
            return Observable.FromEventPattern<MouseEventArgs>(uiElement, "MouseEnter");
        }

        public static IObservable<EventPattern<MouseEventArgs>> GetMouseLeave(this UIElement uiElement)
        {
            return Observable.FromEventPattern<MouseEventArgs>(uiElement, "MouseLeave");
        }

        public static IObservable<EventPattern<MouseEventArgs>> GetMouseMove(this UIElement uiElement)
        {
            return Observable.FromEventPattern<MouseEventArgs>(uiElement, "MouseMove");
        }

        public static IObservable<EventPattern<MouseWheelEventArgs>> GetMouseWheel(this UIElement uiElement)
        {
            return Observable.FromEventPattern<MouseWheelEventArgs>(uiElement, "MouseWheel");
        }

        public static IObservable<EventPattern<MouseButtonEventArgs>> GetMouseLeftButtonDown(this UIElement uiElement)
        {
            return Observable.FromEventPattern<MouseButtonEventArgs>(uiElement, "MouseLeftButtonDown");
        }

        public static IObservable<EventPattern<MouseButtonEventArgs>> GetMouseLeftButtonUp(this UIElement uiElement)
        {
            return Observable.FromEventPattern<MouseButtonEventArgs>(uiElement, "MouseLeftButtonUp");
        }

        public static IObservable<EventPattern<MouseButtonEventArgs>> GetMouseRightButtonDown(this UIElement uiElement)
        {
            return Observable.FromEventPattern<MouseButtonEventArgs>(uiElement, "MouseRightButtonDown");
        }

        public static IObservable<EventPattern<MouseButtonEventArgs>> GetMouseRightButtonUp(this UIElement uiElement)
        {
            return Observable.FromEventPattern<MouseButtonEventArgs>(uiElement, "MouseRightButtonUp");
        }

        public static IObservable<EventPattern<KeyEventArgs>> GetKeyUp(this UIElement uiElement)
        {
            return Observable.FromEventPattern<KeyEventArgs>(uiElement, "KeyUp");
        }

        public static IObservable<EventPattern<KeyEventArgs>> GetKeyDown(this UIElement uiElement)
        {
            return Observable.FromEventPattern<KeyEventArgs>(uiElement, "KeyDown");
        }

        public static IObservable<EventPattern<DragEventArgs>> GetDragEnter(this UIElement uiElement)
        {
            return Observable.FromEventPattern<DragEventArgs>(uiElement, "DragEnter");
        }

        public static IObservable<EventPattern<DragEventArgs>> GetDragLeave(this UIElement uiElement)
        {
            return Observable.FromEventPattern<DragEventArgs>(uiElement, "DragLeave");
        }
        
        public static IObservable<EventPattern<DragEventArgs>> GetDragOver(this UIElement uiElement)
        {
            return Observable.FromEventPattern<DragEventArgs>(uiElement, "DragOver");
        }

        public static IObservable<EventPattern<DragEventArgs>> GetDrop(this UIElement uiElement)
        {
            return Observable.FromEventPattern<DragEventArgs>(uiElement, "Drop");
        }

        public static IObservable<EventPattern<RoutedEventArgs>> GetGotFocus(this UIElement uiElement)
        {
            return Observable.FromEventPattern<RoutedEventArgs>(uiElement, "GotFocus");
        }

        public static IObservable<EventPattern<RoutedEventArgs>> GetLostFocus(this UIElement uiElement)
        {
            return Observable.FromEventPattern<RoutedEventArgs>(uiElement, "LostFocus");
        }
    }
}
